/* * @(#)JTextComponent.java 1.101 98/04/10 * * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved. * * This software is the confidential and proprietary information of Sun * Microsystems, Inc. ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Sun. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING * THIS SOFTWARE OR ITS DERIVATIVES. * */ package com.sun.java.swing.text; import java.util.Hashtable; import java.util.Enumeration; import java.io.*; import java.awt.*; import java.awt.event.*; import java.awt.datatransfer.*; import java.text.*; import com.sun.java.swing.*; import com.sun.java.swing.event.*; import com.sun.java.swing.plaf.*; import com.sun.java.accessibility.*; /** * JTextComponent is the base class for swing text components. It * tries to be compatible with the java.awt.TextComponent class * where it can reasonably do so. Also provided are other services * for additional flexibility (beyond the pluggable UI and bean * support). *
* Text components provide a number of commands that can be used * to manipulate the component. This is essentially the way that * the component expresses its capabilities. These are expressed * in terms of the swing Action interface, using the TextAction * implementation. The set of commands supported by the text * component can be found with the * getActions method. These actions * can be bound to key events, fired from buttons, etc. * *
* To facilitate flexible use of the keyboard, support for * creating keymaps and binding various keystrokes to some kind of * action is provided. In order to allow keymaps to be shared across * multiple text components, they can use actions that extend TextAction. * TextAction can determine which JTextComponent most recently has or had * focus and therefore is the subject of the action (In the case that the * ActionEvent sent to the action doesn't contain the target text component * as its source). *
* The use of the keymap is encouraged, but backward compatibilty * with the awt mechanism is provided by giving the * listeners a chance to steal the event by consuming it. * Keyboard event distribution is handled in the following order, with * each distribution capable of consuming the event. *
* By default the component will create a keymap (named DEFAULT_KEYMAP) * that is shared by all JTextComponent instances as the default keymap. * Typically a look-and-feel implementation will install a different keymap * that resolves to the default keymap for those bindings not found in the * different keymap. The minimal bindings include: *
* The text components have a model-view split. A text component pulls * together the objects used to represent the model, view, and controller. * The text document model may be shared by other views which act as observers * of the model (e.g. a document may be shared by multiple components). * *
* The model is defined by the * Document * interface. This is intended to provide a flexible text storage mechanism * that tracks change during edits and can be extended to more sophisticated * models. The model interfaces are meant to capture the capabilities of * expression given by SGML, a system used to express a wide variety of * content. * Each modification to the document causes notification of the * details of the change to be sent to all observers in the form of a * DocumentEvent * which allows the views to stay up to date with the model. * This event is sent to observers that have implemented the * DocumentListener * interface and registered interest with the model being observed. * *
* Warning: serialized objects of this class will not be compatible with * future swing releases. The current serialization support is appropriate * for short term storage or RMI between Swing1.0 applications. It will * not be possible to load serialized Swing1.0 objects with future releases * of Swing. The JDK1.2 release of Swing will be the compatibility * baseline for the serialized form of Swing objects. * * @beaninfo * attribute: isContainer false * * @author Timothy Prinzing * @version 1.101 04/10/98 * @see Document * @see DocumentEvent * @see DocumentListener * @see Caret * @see CaretEvent * @see CaretListener * @see TextUI * @see View * @see ViewFactory */ public abstract class JTextComponent extends JComponent implements Scrollable, Accessible { /** * Creates a new JTextComponent. * Listeners for caret events are established, and the pluggable * UI installed. The component is marked as editable. No layout manager * is used, because layout is managed by the view subsystem of text. * The document model is set to null. */ public JTextComponent() { super(); enableEvents(AWTEvent.KEY_EVENT_MASK); caretEvent = new MutableCaretEvent(this); addMouseListener(caretEvent); addFocusListener(caretEvent); setEditable(true); setLayout(null); // layout is managed by View hierarchy updateUI(); } /** * Fetches the user-interface factory for this text-oriented editor. * * @return the factory */ public TextUI getUI() { return (TextUI)ui; } /** * Sets the user-interface factory for this text-oriented editor * * @param ui the factory */ public void setUI(TextUI ui) { super.setUI(ui); } /** * Reloads the pluggable UI. The key used to fetch the * new interface is getUIClassID(). The type of * the UI is TextUI. invalidate() is called after * setting the UI. */ public void updateUI() { setUI((TextUI)UIManager.getUI(this)); invalidate(); } /** * Returns true if this component is completely opaque. * This is used in painting backgrounds. * * @return true if this component is completely opaque. */ public boolean isOpaque() { return opaque; } /** * Sets whether or not the UI should render a background. * * @param o true if should render a background */ public void setOpaque(boolean o) { opaque = o; } /** * Adds a caret listener for notification of any changes * to the caret. * * @param listener the listener * @see com.sun.java.swing.event.CaretEvent */ public void addCaretListener(CaretListener listener) { listenerList.add(CaretListener.class, listener); } /** * Removes a caret listener. * * @param listener the listener * @see com.sun.java.swing.event.CaretEvent */ public void removeCaretListener(CaretListener listener) { listenerList.remove(CaretListener.class, listener); } /** * Notifies all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. The listener list is processed in a * last-to-first manner. * * @param e the event * @see EventListenerList */ protected void fireCaretUpdate(CaretEvent e) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==CaretListener.class) { ((CaretListener)listeners[i+1]).caretUpdate(e); } } } /** * Associates the editor with a text document. * The currently registered factory is used to build a view for * the document, which gets displayed by the editor after revalidation. * A PropertyChange event ("document") is propagated to each listener. * * @param doc the document to display/edit * @see #getDocument * @beaninfo * description: the text document model * bound: true * expert: true */ public void setDocument(Document doc) { if (accessibleContext != null) { model.removeDocumentListener( ((AccessibleJTextComponent)accessibleContext)); } Document old = model; model = doc; firePropertyChange("document", old, doc); revalidate(); repaint(); if (accessibleContext != null) { model.addDocumentListener( ((AccessibleJTextComponent)accessibleContext)); } } /** * Fetches the model associated with the editor. This is * primarily for the UI to get at the minimal amount of * state required to be a text editor. Subclasses will * return the actual type of the model which will typically * be something that extends Document. * * @return the model */ public Document getDocument() { return model; } /** * Fetches the command list for the editor. This is * the list of commands supported by the plugged-in UI * augmented by the collection of commands that the * editor itself supports. These are useful for binding * to events, such as in a keymap. * * @return the command list */ public Action[] getActions() { return getUI().getEditorKit().getActions(); } /** * Sets margin space between the text component's border * and its text. Setting it to null will cause the text component * to use a default margin. The text component's default Border * object will use this value to create the proper margin. * However, if a non-default border is set on the text component, * it is that Border object's responsibility to create the * appropriate margin space (else this property will effectively * be ignored). This causes a redraw of the component. * A PropertyChange event ("margin") is sent to all listeners. * * @param m the space between the border and the text * @beaninfo * description: desired space between the border and text area * bound: true */ public void setMargin(Insets m) { Insets old = margin; margin = m; firePropertyChange("margin", old, m); invalidate(); } /** * Returns the margin between the text component's border and * its text. If no margin has been set, a default value from the UI * is returned. * * @return the margin */ public Insets getMargin() { if(margin == null) { return ((TextUI)ui).getDefaultMargin(); } else { return margin; } } /** * Fetches the caret that allows text-oriented navigation over * the view. * * @return the caret */ public Caret getCaret() { return caret; } /** * Sets the caret to be used. By default this will be set * by the UI that gets installed. This can be changed to * a custom caret if desired. Setting the caret results in a * PropertyChange event ("caret") being fired. * * @param c the caret * @see #getCaret * @beaninfo * description: the caret used to select/navigate * bound: true * expert: true */ public void setCaret(Caret c) { if (caret != null) { caret.removeChangeListener(caretEvent); caret.deinstall(this); } Caret old = caret; caret = c; if (caret != null) { caret.install(this); caret.addChangeListener(caretEvent); } firePropertyChange("caret", old, caret); } /** * Fetches the object responsible for making highlights. * * @return the highlighter */ public Highlighter getHighlighter() { return highlighter; } /** * Sets the highlighter to be used. By default this will be set * by the UI that gets installed. This can be changed to * a custom highlighter if desired. The highlighter can be set to * null to disable it. A PropertyChange event ("highlighter") is fired * when a new highlighter is installed. * * @param h the highlighter * @see #getHighlighter * @beaninfo * description: object responsible for background highlights * bound: true * expert: true */ public void setHighlighter(Highlighter h) { if (highlighter != null) { highlighter.deinstall(this); } Highlighter old = highlighter; highlighter = h; if (highlighter != null) { highlighter.install(this); } firePropertyChange("highlighter", old, h); } /** * Sets the keymap to use for binding events to * actions. Setting to null effectively disables keyboard input. * A PropertyChange event ("keymap") is fired when a new keymap * is installed. * * @param map the keymap * @see #getKeymap * @beaninfo * description: set of key event to action bindings to use * bound: true */ public void setKeymap(Keymap map) { Keymap old = keymap; keymap = map; firePropertyChange("keymap", old, keymap); } /** * Fetches the keymap currently active in this text * component. * * @return the keymap */ public Keymap getKeymap() { return keymap; } /** * Adds a new keymap into the keymap hierarchy. Keymap bindings * resolve from bottom up so an attribute specified in a child * will override an attribute specified in the parent. * * @param nm the name of the keymap (must be unique within the * collection of named keymaps in the document). The name may * be null if the keymap is unnamed, but the caller is responsible * for managing the reference returned as an unnamed keymap can't * be fetched by name. * @param parent the parent keymap. This may be null if unspecified * bindings need not be resolved in some other keymap. * @return the keymap */ public static Keymap addKeymap(String nm, Keymap parent) { Keymap map = new DefaultKeymap(nm, parent); if (nm != null) { // add a named keymap, a class of bindings keymapTable.put(nm, map); } return map; } /** * Removes a named keymap previously added to the document. Keymaps * with null names may not be removed in this way. * * @param nm the name of the keymap to remove * @return the keymap that was removed */ public static Keymap removeKeymap(String nm) { return (Keymap) keymapTable.remove(nm); } /** * Fetches a named keymap previously added to the document. * This does not work with null-named keymaps. * * @param nm the name of the keymap * @return the keymap */ public static Keymap getKeymap(String nm) { return (Keymap) keymapTable.get(nm); } /** * Binding record for creating key bindings. *
* Warning: serialized objects of this class will not be compatible with * future swing releases. The current serialization support is appropriate * for short term storage or RMI between Swing1.0 applications. It will * not be possible to load serialized Swing1.0 objects with future releases * of Swing. The JDK1.2 release of Swing will be the compatibility * baseline for the serialized form of Swing objects. */ public static class KeyBinding { /** * The key. */ public KeyStroke key; /** * The name of the action for the key. */ public String actionName; /** * Creates a new key binding. * * @param key the key * @param actionName the name of the action for the key */ public KeyBinding(KeyStroke key, String actionName) { this.key = key; this.actionName = actionName; } } /** *
* Loads a keymap with a bunch of * bindings. This can be used to take a static table of * definitions and load them into some keymap. The following * example illustrates an example of binding some keys to * the cut, copy, and paste actions associated with a * JTextComponent. A code fragment to accomplish * this might look as follows: *
*
* static final JTextComponent.KeyBinding[] defaultBindings = {
* new JTextComponent.KeyBinding(
* KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
* DefaultEditorKit.copyAction),
* new JTextComponent.KeyBinding(
* KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
* DefaultEditorKit.pasteAction),
* new JTextComponent.KeyBinding(
* KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK),
* DefaultEditorKit.cutAction),
* };
*
* JTextComponent c = new JTextPane();
* Keymap k = c.getKeymap();
* JTextComponent.loadKeymap(k, defaultBindings, c.getActions());
*
*
* The sets of bindings and actions may be empty but must be non-null.
*
* @param map the keymap
* @param bindings the bindings
* @param actions the set of actions
*/
public static void loadKeymap(Keymap map, KeyBinding[] bindings, Action[] actions) {
Hashtable h = new Hashtable();
for (int i = 0; i < actions.length; i++) {
Action a = actions[i];
String value = (String)a.getValue(Action.NAME);
h.put((value!=null ? value:""), a);
}
for (int i = 0; i < bindings.length; i++) {
Action a = (Action) h.get(bindings[i].actionName);
if (a != null) {
map.addActionForKeyStroke(bindings[i].key, a);
}
}
}
/**
* Maps an event to an action if one is defined in the
* installed keymap, and perform the action. If the action is
* performed, the event is consumed.
*
* @returns true if an action was performed, false otherwise.
*/
private final boolean mapEventToAction(KeyEvent e) {
Keymap binding = getKeymap();
if (binding != null) {
KeyStroke k = KeyStroke.getKeyStrokeForEvent(e);
Action a = binding.getAction(k);
if (a != null) {
String command = null;
if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
command = String.valueOf(e.getKeyChar());
}
ActionEvent ae = new ActionEvent(this,
ActionEvent.ACTION_PERFORMED,
command, e.getModifiers());
a.actionPerformed(ae);
e.consume();
return true;
}
}
return false;
}
/**
* Fetches the current color used to render the
* caret.
*
* @return the color
*/
public Color getCaretColor() {
return caretColor;
}
/**
* Sets the current color used to render the
* caret. Setting to null effectively restores the default color.
* Setting the color results in a PropertyChange event ("caretColor")
* being fired.
*
* @param c the color
* @see #getCaretColor
* @beaninfo
* description: the color used to render the caret
* bound: true
* preferred: true
*/
public void setCaretColor(Color c) {
Color old = caretColor;
caretColor = c;
firePropertyChange("caretColor", old, caretColor);
}
/**
* Fetches the current color used to render the
* selection.
*
* @return the color
*/
public Color getSelectionColor() {
return selectionColor;
}
/**
* Sets the current color used to render the
* selection. Setting the color to null is the same as setting
* Color.white. Setting the color results in a PropertyChange event
* ("selectionColor").
*
* @param c the color
* @see #getSelectionColor
* @beaninfo
* description: color used to render selection background
* bound: true
* preferred: true
*/
public void setSelectionColor(Color c) {
Color old = selectionColor;
selectionColor = c;
firePropertyChange("selectionColor", old, selectionColor);
}
/**
* Fetches the current color used to render the
* selected text.
*
* @return the color
*/
public Color getSelectedTextColor() {
return selectedTextColor;
}
/**
* Sets the current color used to render the
* selected text. Setting the color to null is the same as Color.black.
* Setting the color results in a PropertyChange event
* ("selectedTextColor") being fired.
*
* @param c the color
* @see #getSelectedTextColor
* @beaninfo
* description: color used to render selected text
* bound: true
* preferred: true
*/
public void setSelectedTextColor(Color c) {
Color old = selectedTextColor;
selectedTextColor = c;
firePropertyChange("selectedTextColor", old, selectedTextColor);
}
/**
* Fetches the current color used to render the
* selected text.
*
* @return the color
*/
public Color getDisabledTextColor() {
return disabledTextColor;
}
/**
* Sets the current color used to render the
* disabled text. Setting the color fires off a
* PropertyChange event ("disabledTextColor").
*
* @param c the color
* @see #getDisabledTextColor
* @beaninfo
* description: color used to render disabled text
* bound: true
* preferred: true
*/
public void setDisabledTextColor(Color c) {
Color old = disabledTextColor;
disabledTextColor = c;
firePropertyChange("disabledTextColor", old, disabledTextColor);
}
/**
* Replaces the currently selected content with new content
* represented by the given string. If there is no selection
* this amounts to an insert of the given text. If there
* is no replacement text this amounts to a removal of the
* current selection.
* * This is the method that is used by the default implementation * of the action for inserting content that gets bound to the * keymap actions. *
* This method is thread safe, although most Swing methods * are not. Please see * Threads * and Swing for more information. *
* If the component is not currently editable, beep and return. Then if
* the underlying model is null, do nothing.
*
* @param content the content to replace the selection with
*/
public void replaceSelection(String content) {
if (! isEditable()) {
getToolkit().beep();
return;
}
Document doc = getDocument();
if (doc != null) {
try {
int p0 = Math.min(caret.getDot(), caret.getMark());
int p1 = Math.max(caret.getDot(), caret.getMark());
if (p0 != p1) {
doc.remove(p0, p1 - p0);
}
if (content != null && content.length() > 0) {
doc.insertString(p0, content, null);
}
} catch (BadLocationException e) {
getToolkit().beep();
}
}
}
/**
* Fetches a portion of the text represented by the
* component. Returns an empty string if length is 0.
*
* @param offs the offset >= 0
* @param len the length >= 0
* @return the text
* @exception BadLocationException if the offset or length are invalid
*/
public String getText(int offs, int len) throws BadLocationException {
return getDocument().getText(offs, len);
}
/**
* Converts the given location in the model to a place in
* the view coordinate system.
*
* @param pos the position >= 0
* @return the coordinates as a rectangle, with (r.x, r.y) as the location
* in the coordinate system
* @exception BadLocationException if the given position does not
* represent a valid location in the associated document
* @see TextUI#modelToView
*/
public Rectangle modelToView(int pos) throws BadLocationException {
return getUI().modelToView(pos);
}
/**
* Converts the given place in the view coordinate system
* to the nearest representative location in the model.
*
* @param pt the location in the view to translate
* @return the offset >= 0 from the start of the document
* @see TextUI#viewToModel
*/
public int viewToModel(Point pt) {
return getUI().viewToModel(pt);
}
/**
* Transfers the currently selected range in the associated
* text model to the system clipboard, removing the contents
* from the model. The current selection is reset. Does nothing
* for null selections.
*/
public void cut() {
try {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
int p0 = Math.min(caret.getDot(), caret.getMark());
int p1 = Math.max(caret.getDot(), caret.getMark());
if (p0 != p1) {
Document doc = getDocument();
String srcData = doc.getText(p0, p1 - p0);
StringSelection contents = new StringSelection(srcData);
clipboard.setContents(contents, defaultClipboardOwner);
doc.remove(p0, p1 - p0);
}
} catch (BadLocationException e) {
}
}
/**
* Transfers the currently selected range in the associated
* text model to the system clipboard, leaving the contents
* in the text model. The current selection is remains intact.
* Does nothing for null selections.
*/
public void copy() {
try {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
int p0 = Math.min(caret.getDot(), caret.getMark());
int p1 = Math.max(caret.getDot(), caret.getMark());
if (p0 != p1) {
Document doc = getDocument();
String srcData = doc.getText(p0, p1 - p0);
StringSelection contents = new StringSelection(srcData);
clipboard.setContents(contents, defaultClipboardOwner);
}
} catch (BadLocationException e) {
}
}
/**
* Transfers the contents of the system clipboard into the
* associated text model. If there is a selection in the
* associated view, it is replaced with the contents of the
* clipboard. If there is no selection, the clipboard contents
* are inserted in front of the current insert position in
* the associated view. If the clipboard is empty, does nothing.
* @see #replaceSelection
*/
public void paste() {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable content = clipboard.getContents(this);
if (content != null) {
try {
String dstData = (String)(content.getTransferData(DataFlavor.stringFlavor));
replaceSelection(dstData);
} catch (Exception e) {
System.err.println("Couldn't get clipboard contents in format: "+
DataFlavor.stringFlavor.getHumanPresentableName());
}
}
}
/**
* Moves the caret to a new position, leaving behind a
* mark defined by the last time setCaretPosition was
* called. This forms a selection.
*
* @param pos the position
* @see #setCaretPosition
*/
public void moveCaretPosition(int pos) {
caret.moveDot(pos);
}
/**
* The bound property name for the focus accelerator.
*/
public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
/**
* Sets the key accelerator that will cause the receiving text
* component to get the focus. The accelerator will be the
* key combination of the alt key and the character
* given (converted to upper case). By default, there is no focus
* accelerator key. Any previous key accelerator setting will be
* superseded. A '\0' key setting will be registered, and has the
* effect of turning off the focus accelerator. When the new key
* is set, a PropertyChange event (FOCUS_ACCELERATOR_KEY) will be fired.
*
* @param aKey the key
* @see getFocusAccelerator
* @beaninfo
* description: accelerator character used to grab focus
* bound: true
*/
public void setFocusAccelerator(char aKey) {
aKey = Character.toUpperCase(aKey);
KeyStroke[] keyStrokes = getRegisteredKeyStrokes();
int i,c;
for(i=0,c=keyStrokes.length;i
* This is implemented to take a default action, typically
* inserting the character into the document as content. Subclasses
* would normally override this method if they process some
* key events themselves. If the event is processed,
* it should be consumed.
*
* @param e the event
*/
protected void processComponentKeyEvent(KeyEvent e) {
int id = e.getID();
switch(id) {
case KeyEvent.KEY_TYPED:
if (mapEventToAction(e) == false) {
// default behavior is to input translated
// characters as content if the character
// hasn't been mapped in the keymap.
Keymap binding = getKeymap();
if (binding != null) {
Action a = binding.getDefaultAction();
if (a != null) {
ActionEvent ae = new ActionEvent(this,
ActionEvent.ACTION_PERFORMED,
String.valueOf(e.getKeyChar()),
e.getModifiers());
a.actionPerformed(ae);
e.consume();
}
}
}
break;
case KeyEvent.KEY_PRESSED:
mapEventToAction(e);
break;
case KeyEvent.KEY_RELEASED:
mapEventToAction(e);
break;
}
}
// --- java.awt.TextComponent methods ------------------------
/**
* Sets the position of the text insertion caret for the TextComponent.
* Note that the caret tracks change, so this may move if the underlying
* text of the component is changed. If the document is null, does
* nothing.
*
* @param position the position
* @beaninfo
* description: the caret position
*/
public void setCaretPosition(int position) {
Document doc = getDocument();
if (doc != null) {
if (position > doc.getLength() || position < 0) {
throw new IllegalArgumentException("bad position: " + position);
}
caret.setDot(position);
}
}
/**
* Returns the position of the text insertion caret for the
* text component.
*
* @return the position of the text insertion caret for the
* text component >= 0
*/
public int getCaretPosition() {
return caret.getDot();
}
/**
* Sets the text of this TextComponent to the specified text. If the
* text is null or empty, has the effect of simply deleting the old text.
*
* This method is thread safe, although most Swing methods
* are not. Please see
* Threads
* and Swing for more information.
*
* @param t the new text to be set
* @see #getText
* @beaninfo
* description: the text of this component
*/
public void setText(String t) {
try {
Document doc = getDocument();
doc.remove(0, doc.getLength());
doc.insertString(0, t, null);
} catch (BadLocationException e) {
getToolkit().beep();
}
}
/**
* Returns the text contained in this TextComponent. If the underlying
* document is null, will give a NullPointerException.
*
* @return the text
* @see #setText
*/
public String getText() {
Document doc = getDocument();
String txt;
try {
txt = doc.getText(0, doc.getLength());
} catch (BadLocationException e) {
txt = null;
}
return txt;
}
/**
* Returns the selected text contained in this TextComponent. If
* the selection is null or the document empty, returns null.
*
* @return the text
* @exception IllegalArgumentException if the selection doesn't
* have a valid mapping into the document for some reason
* @see #setText
*/
public String getSelectedText() {
String txt = null;
int p0 = Math.min(caret.getDot(), caret.getMark());
int p1 = Math.max(caret.getDot(), caret.getMark());
if (p0 != p1) {
try {
Document doc = getDocument();
txt = doc.getText(p0, p1 - p0);
} catch (BadLocationException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
return txt;
}
/**
* Returns the boolean indicating whether this TextComponent is
* editable or not.
*
* @return the boolean value
* @see #setEditable
*/
public boolean isEditable() {
return editable;
}
/**
* Sets the specified boolean to indicate whether or not this
* TextComponent should be editable. A PropertyChange event ("editable")
* is fired when the state is changed.
*
* @param b the boolean to be set
* @see #isEditable
* @beaninfo
* description: specifies if the text can be edited
*/
public void setEditable(boolean b) {
boolean oldVal = editable;
editable = b;
firePropertyChange("editable", new Boolean(oldVal), new Boolean(editable));
}
/**
* Returns the selected text's start position. Return 0 for an
* empty document, or the value of dot if no selection.
*
* @return the start position >= 0
*/
public int getSelectionStart() {
int start = Math.min(caret.getDot(), caret.getMark());
return start;
}
/**
* Sets the selection start to the specified position. The new
* starting point is constrained to be before or at the current
* selection end.
*
* This is available for backward compatiblitity to code
* that called this method on java.awt.TextComponent. This is
* implemented to forward to the Caret implementation which
* is where the actual selection is maintained.
*
* @param selectionStart the start position of the text >= 0
* @beaninfo
* description: starting location of the selection.
*/
public void setSelectionStart(int selectionStart) {
/* Route through select method to enforce consistent policy
* between selectionStart and selectionEnd.
*/
select(selectionStart, getSelectionEnd());
}
/**
* Returns the selected text's end position. Return 0 if the document
* is empty, or the value of dot if there is no selection.
*
* @return the end position >= 0
*/
public int getSelectionEnd() {
int end = Math.max(caret.getDot(), caret.getMark());
return end;
}
/**
* Sets the selection end to the specified position. The new
* end point is constrained to be at or after the current
* selection start.
*
* This is available for backward compatiblitity to code
* that called this method on java.awt.TextComponent. This is
* implemented to forward to the Caret implementation which
* is where the actual selection is maintained.
*
* @param selectionEnd the end position of the text >= 0
* @beaninfo
* description: ending location of the selection.
*/
public void setSelectionEnd(int selectionEnd) {
/* Route through select method to enforce consistent policy
* between selectionStart and selectionEnd.
*/
select(getSelectionStart(), selectionEnd);
}
/**
* Selects the text found between the specified start and end
* locations. This call is provided for backward compatibility.
* It is routed to a call to setCaretPosition
* followed by a call to moveCaretPostion. The preferred way
* to manage selection is by calling those methods directly.
*
* @param selectionStart the start position of the text >= 0
* @param selectionEnd the end position of the text >= 0
* @see setCaretPosition
* @see moveCaretPosition
*/
public void select(int selectionStart, int selectionEnd) {
setCaretPosition(selectionStart);
moveCaretPosition(selectionEnd);
}
/**
* Selects all the text in the TextComponent. Does nothing on a null
* or empty document.
*/
public void selectAll() {
Document doc = getDocument();
if (doc != null) {
setCaretPosition(0);
moveCaretPosition(doc.getLength());
}
}
// --- Scrollable methods ---------------------------------------------
/**
* Returns the preferred size of the viewport for a view component.
* This is implemented to do the default behavior of returning
* the preferred size of the component.
*
* @return The preferredSize of a JViewport whose view is this Scrollable.
*/
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
/**
* Components that display logical rows or columns should compute
* the scroll increment that will completely expose one new row
* or column, depending on the value of orientation. Ideally,
* components should handle a partially exposed row or column by
* returning the distance required to completely expose the item.
*
* The default implementation of this is to simply return 10% of
* the visible area. Subclasses are likely to be able to provide
* a much more reasonable value.
*
* @param visibleRect The view area visible within the viewport
* @param orientation Either SwingConstants.VERTICAL or
* SwingConstants.HORIZONTAL.
* @param direction Less than zero to scroll up/left, greater than
* zero for down/right.
* @return The "unit" increment for scrolling in the specified direction
* @exception IllegalArgumentException for an invalid orientation
* @see JScrollBar#setUnitIncrement
*/
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
switch(orientation) {
case SwingConstants.VERTICAL:
return visibleRect.height / 10;
case SwingConstants.HORIZONTAL:
return visibleRect.width / 10;
default:
throw new IllegalArgumentException("Invalid orientation: " + orientation);
}
}
/**
* Components that display logical rows or columns should compute
* the scroll increment that will completely expose one block
* of rows or columns, depending on the value of orientation.
*
* The default implementation of this is to simply return the visible
* area. Subclasses will likely be able to provide a much more
* reasonable value.
*
* @param visibleRect The view area visible within the viewport
* @param orientation Either SwingConstants.VERTICAL or
* SwingConstants.HORIZONTAL.
* @param direction Less than zero to scroll up/left, greater than zero
* for down/right.
* @return The "block" increment for scrolling in the specified direction.
* @exception IllegalArgumentException for an invalid orientation
* @see JScrollBar#setBlockIncrement
*/
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
switch(orientation) {
case SwingConstants.VERTICAL:
return visibleRect.height;
case SwingConstants.HORIZONTAL:
return visibleRect.width;
default:
throw new IllegalArgumentException("Invalid orientation: " + orientation);
}
}
/**
* Return true if a viewport should always force the width of this
* Scrollable to match the width of the viewport. For example a normal
* text view that supported line wrapping would return true here, since it
* would be undesirable for wrapped lines to disappear beyond the right
* edge of the viewport. Note that returning true for a Scrollable
* whose ancestor is a JScrollPane effectively disables horizontal
* scrolling.
*
* Scrolling containers, like JViewport, will use this method each
* time they are validated.
*
* @return true if a viewport should force the Scrollables
* width to match its own.
*/
public boolean getScrollableTracksViewportWidth() {
return false;
}
/**
* Return true if a viewport should always force the height of this
* Scrollable to match the height of the viewport. For example a
* columnar text view that flowed text in left to right columns
* could effectively disable vertical scrolling by returning
* true here.
*
* Scrolling containers, like JViewport, will use this method each
* time they are validated.
*
* @return true if a viewport should force the Scrollables height
* to match its own.
*/
public boolean getScrollableTracksViewportHeight() {
return false;
}
/////////////////
// Accessibility support
////////////////
/**
* Gets the AccessibleContext associated with this JComponent.
* A new context is created if necessary.
*
* @return the AccessibleContext of this JComponent
*/
public AccessibleContext getAccessibleContext() {
if (accessibleContext == null) {
accessibleContext = new AccessibleJTextComponent();
}
return accessibleContext;
}
/**
* Accessibility implementation for JTextComponent.
*
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*/
public class AccessibleJTextComponent extends AccessibleJComponent
implements AccessibleText, CaretListener, DocumentListener {
int caretPos;
/**
* Constructs an AccessibleJTextComponent. Adds a listener to track
* caret change.
*/
public AccessibleJTextComponent() {
Document doc = JTextComponent.this.getDocument();
if (doc != null) {
doc.addDocumentListener(this);
}
JTextComponent.this.addCaretListener(this);
caretPos = getCaretPosition();
}
/**
* Handles caret updates (fire appropriate property change event,
* which are AccessibleContext.ACCESSIBLE_CARET_PROPERTY and
* AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY).
* This keeps track of the dot position internally. When the caret
* moves, the internal position is updated after firing the event.
*
* @param e the CaretEvent
*/
public void caretUpdate(CaretEvent e) {
int dot = e.getDot();
int mark = e.getMark();
if (caretPos != dot) {
// the caret moved
firePropertyChange(ACCESSIBLE_CARET_PROPERTY,
new Integer(caretPos), new Integer(dot));
caretPos = dot;
}
if (mark != dot) {
// there is a selection
firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
getSelectedText());
}
}
// DocumentListener methods
/**
* Handles document insert (fire appropriate property change event
* which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
* This tracks the dot via the event.
*
* @param e the DocumentEvent
*/
public void insertUpdate(DocumentEvent e) {
Caret c = JTextComponent.this.getCaret();
Integer dot = new Integer(c.getDot());
firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, dot);
}
/**
* Handles document remove (fire appropriate property change event,
* which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
* This tracks the dot via the event.
*
* @param e the DocumentEvent
*/
public void removeUpdate(DocumentEvent e) {
Caret c = JTextComponent.this.getCaret();
Integer dot = new Integer(c.getDot());
firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, dot);
}
/**
* Handles document remove (fire appropriate property change event,
* which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
* This tracks the dot via the event.
*
* @param e the DocumentEvent
*/
public void changedUpdate(DocumentEvent e) {
Caret c = JTextComponent.this.getCaret();
Integer dot = new Integer(c.getDot());
firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, dot);
}
/**
* Gets the state set of the JTextComponent.
* The AccessibleStateSet of an object is composed of a set of
* unique AccessibleState's. A change in the AccessibleStateSet
* of an object will cause a PropertyChangeEvent to be fired
* for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property.
*
* @return an instance of AccessibleStateSet containing the
* current state set of the object
* @see AccessibleStateSet
* @see AccessibleState
* @see #addPropertyChangeListener
*/
public AccessibleStateSet getAccessibleStateSet() {
AccessibleStateSet states = super.getAccessibleStateSet();
if (JTextComponent.this.isEditable()) {
states.add(AccessibleState.EDITABLE);
}
return states;
}
/**
* Gets the role of this object.
*
* @return an instance of AccessibleRole describing the role of the
* object (AccessibleRole.TEXT)
* @see AccessibleRole
*/
public AccessibleRole getAccessibleRole() {
return AccessibleRole.TEXT;
}
/**
* Gets the AccessibleText interface associated with this object.
*
* @return an instance of AccessibleText
*/
public AccessibleText getAccessibleText() {
return this;
}
// --- interface AccessibleText methods ------------------------
/**
* Many of these methods are just convenience methods; they
* just call the equivalent on the parent
*/
/**
* Given a point in local coordinates, return the zero-based index
* of the character under that Point. If the point is invalid,
* this method returns -1.
*
* @param p the Point in local coordinates
* @return the zero-based index of the character under Point p.
*/
public int getIndexAtPoint(Point p) {
if (p == null) {
return -1;
}
return JTextComponent.this.viewToModel(p);
}
/**
* Determines the bounding box of the character at the given
* index into the string. The bounds are returned in local
* coordinates. If the index is invalid a null rectangle
* is returned.
*
* @param i the index into the String >= 0
* @return the screen coordinates of the character's bounding box
*/
public Rectangle getCharacterBounds(int i) {
if (i < 0 || i > model.getLength()-1) {
return null;
}
Rectangle rect;
try {
rect = modelToView(i);
} catch (BadLocationException e) {
rect = null;
}
return rect;
}
/**
* Returns the number of characters (valid indicies)
*
* @return the number of characters >= 0
*/
public int getCharCount() {
return model.getLength();
}
/**
* Returns the zero-based offset of the caret.
*
* Note: The character to the right of the caret will have the
* same index value as the offset (the caret is between
* two characters).
*
* @return the zero-based offset of the caret.
*/
public int getCaretPosition() {
return JTextComponent.this.getCaretPosition();
}
/**
* Returns the AttributeSet for a given character (at a given index).
*
* @param i the zero-based index into the text
* @return the AttributeSet of the character
*/
public AttributeSet getCharacterAttribute(int i) {
Element e = null;
for (e = model.getDefaultRootElement(); ! e.isLeaf(); ) {
int index = e.getElementIndex(i);
e = e.getElement(index);
}
return e.getAttributes();
}
/**
* Returns the start offset within the selected text.
* If there is no selection, but there is
* a caret, the start and end offsets will be the same.
* Return 0 if the text is empty, or the caret position
* if no selection.
*
* @return the index into the text of the start of the selection >= 0
*/
public int getSelectionStart() {
return JTextComponent.this.getSelectionStart();
}
/**
* Returns the end offset within the selected text.
* If there is no selection, but there is
* a caret, the start and end offsets will be the same.
* Return 0 if the text is empty, or the caret position
* if no selection.
*
* @return the index into teh text of the end of the selection >= 0
*/
public int getSelectionEnd() {
return JTextComponent.this.getSelectionEnd();
}
/**
* Returns the portion of the text that is selected.
*
* @return the text, null if no selection
*/
public String getSelectedText() {
return JTextComponent.this.getSelectedText();
}
/**
* Returns the String at a given index.
*
* @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
* or AccessibleText.SENTENCE to retrieve
* @param index an index within the text >= 0
* @return the letter, word, or sentence,
* null for an invalid index or part
*/
public String getAtIndex(int part, int index) {
if (index < 0 || index > model.getLength()-1) {
return null;
}
switch (part) {
case AccessibleText.CHARACTER:
try {
return model.getText(index, 1);
} catch (BadLocationException e) {
return null;
}
case AccessibleText.WORD:
try {
String s = model.getText(0, model.getLength());
BreakIterator words = BreakIterator.getWordInstance();
words.setText(s);
int end = words.following(index);
return s.substring(words.previous(), end);
} catch (BadLocationException e) {
return null;
}
case AccessibleText.SENTENCE:
try {
String s = model.getText(0, model.getLength());
BreakIterator sentence = BreakIterator.getSentenceInstance();
sentence.setText(s);
int end = sentence.following(index);
return s.substring(sentence.previous(), end);
} catch (BadLocationException e) {
return null;
}
default:
return null;
}
}
/**
* Returns the String after a given index.
*
* @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
* or AccessibleText.SENTENCE to retrieve
* @param index an index within the text >= 0
* @return the letter, word, or sentence, null for an invalid
* index or part
*/
public String getAfterIndex(int part, int index) {
if (index < 0 || index > model.getLength()-1) {
return null;
}
switch (part) {
case AccessibleText.CHARACTER:
try {
return model.getText(index+1, 1);
} catch (BadLocationException e) {
return null;
}
case AccessibleText.WORD:
try {
String s = model.getText(0, model.getLength());
BreakIterator words = BreakIterator.getWordInstance();
words.setText(s);
int start = words.following(index);
return s.substring(start, words.following(start));
} catch (BadLocationException e) {
return null;
}
case AccessibleText.SENTENCE:
try {
String s = model.getText(0, model.getLength());
BreakIterator sentence = BreakIterator.getSentenceInstance();
sentence.setText(s);
int start = sentence.following(index);
return s.substring(start, sentence.following(start));
} catch (BadLocationException e) {
return null;
}
default:
return null;
}
}
/**
* Returns the String before a given index.
*
* @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
* or AccessibleText.SENTENCE to retrieve
* @param index an index within the text >= 0
* @return the letter, word, or sentence, null for an invalid index
* or part
*/
public String getBeforeIndex(int part, int index) {
if (index < 0 || index > model.getLength()-1) {
return null;
}
switch (part) {
case AccessibleText.CHARACTER:
try {
return model.getText(index-1, 1);
} catch (BadLocationException e) {
return null;
}
case AccessibleText.WORD:
try {
String s = model.getText(0, model.getLength());
BreakIterator words = BreakIterator.getWordInstance();
words.setText(s);
int end = words.next(index);
end = words.previous();
return s.substring(words.previous(), end);
} catch (BadLocationException e) {
return null;
}
case AccessibleText.SENTENCE:
try {
String s = model.getText(0, model.getLength());
BreakIterator sentence = BreakIterator.getSentenceInstance();
sentence.setText(s);
int end = sentence.next(index);
end = sentence.previous();
return s.substring(sentence.previous(), end);
} catch (BadLocationException e) {
return null;
}
default:
return null;
}
}
}
// --- serialization ---------------------------------------------
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException
{
s.defaultReadObject();
caretEvent = new MutableCaretEvent(this);
addMouseListener(caretEvent);
addFocusListener(caretEvent);
// PENDING(prinz)
// This should really be updateUI(), but the ui
// is currently being serialized so we'll use it.
getUI().installUI(this);
}
// --- member variables ----------------------------------
/**
* The document model.
*/
private Document model;
/**
* The caret used to display the insert position
* and navigate throught the document.
*
* PENDING(prinz)
* This should be serializable, default installed
* by UI.
*/
private transient Caret caret;
/**
* The object responsible for managing highlights.
*
* PENDING(prinz)
* This should be serializable, default installed
* by UI.
*/
private transient Highlighter highlighter;
/**
* The current key bindings in effect.
*
* PENDING(prinz)
* This should be serializable, default installed
* by UI.
*/
private transient Keymap keymap;
/**
* is the component opaque?
*/
private boolean opaque;
private transient MutableCaretEvent caretEvent;
private Color caretColor;
private Color selectionColor;
private Color selectedTextColor;
private Color disabledTextColor;
private boolean editable;
private Insets margin;
private char focusAccelerator;
private Action focusAction = new FocusAction();
private static ClipboardOwner defaultClipboardOwner = new ClipboardObserver();
static class ClipboardObserver implements ClipboardOwner {
public void lostOwnership(Clipboard clipboard, Transferable contents) {
}
}
/**
* package level access to focused text component
* so that JTextAction implementations can be
* reused across JTextComponent implementations.
*/
static final JTextComponent getFocusedComponent() {
return focusedComponent;
}
private static Hashtable keymapTable = null;
private JTextComponent editor;
private static JTextComponent focusedComponent;
static class DefaultKeymap implements Keymap {
DefaultKeymap(String nm, Keymap parent) {
this.nm = nm;
this.parent = parent;
bindings = new Hashtable();
}
/**
* Fetch the default action to fire if a
* key is typed (ie a KEY_TYPED KeyEvent is received)
* and there is no binding for it. Typically this
* would be some action that inserts text so that
* the keymap doesn't require an action for each
* possible key.
*/
public Action getDefaultAction() {
if (defaultAction != null) {
return defaultAction;
}
return (parent != null) ? parent.getDefaultAction() : null;
}
/**
* Set the default action to fire if a key is typed.
*/
public void setDefaultAction(Action a) {
defaultAction = a;
}
public String getName() {
return nm;
}
public Action getAction(KeyStroke key) {
Action a = (Action) bindings.get(key);
if ((a == null) && (parent != null)) {
a = parent.getAction(key);
}
return a;
}
public KeyStroke[] getBoundKeyStrokes() {
KeyStroke[] keys = new KeyStroke[bindings.size()];
int i = 0;
for (Enumeration e = bindings.keys() ; e.hasMoreElements() ;) {
keys[i++] = (KeyStroke) e.nextElement();
}
return keys;
}
public Action[] getBoundActions() {
Action[] actions = new Action[bindings.size()];
int i = 0;
for (Enumeration e = bindings.elements() ; e.hasMoreElements() ;) {
actions[i++] = (Action) e.nextElement();
}
return actions;
}
public KeyStroke[] getKeyStrokesForAction(Action a) {
// TBD
return null;
}
public boolean isLocallyDefined(KeyStroke key) {
return bindings.containsKey(key);
}
public void addActionForKeyStroke(KeyStroke key, Action a) {
bindings.put(key, a);
}
public void removeKeyStrokeBinding(KeyStroke key) {
bindings.remove(key);
}
public void removeBindings() {
bindings.clear();
}
public Keymap getResolveParent() {
return parent;
}
public void setResolveParent(Keymap parent) {
this.parent = parent;
}
/**
* String representation of the keymap... potentially
* a very long string.
*/
public String toString() {
return "Keymap[" + nm + "]" + bindings;
}
String nm;
Keymap parent;
Hashtable bindings;
Action defaultAction;
}
/**
* This is the name of the default keymap that will be shared by all
* JTextComponent instances unless they have had a different
* keymap set.
*/
public static final String DEFAULT_KEYMAP = "default";
/**
* Default bindings for the default keymap if no other bindings
* are given.
*/
static final KeyBinding[] defaultBindings = {
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0),
DefaultEditorKit.deletePrevCharAction),
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
DefaultEditorKit.deleteNextCharAction),
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
DefaultEditorKit.forwardAction),
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
DefaultEditorKit.backwardAction)
};
static {
try {
keymapTable = new Hashtable(17);
Keymap binding = addKeymap(DEFAULT_KEYMAP, null);
binding.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction());
EditorKit kit = new DefaultEditorKit();
loadKeymap(binding, defaultBindings, kit.getActions());
} catch (Throwable e) {
e.printStackTrace();
keymapTable = new Hashtable(17);
}
}
/**
* Event to use when firing a notification of change to caret
* position. This is mutable so that the event can be reused
* since caret events can be fairly high in bandwidth.
*/
static class MutableCaretEvent extends CaretEvent implements ChangeListener, MouseListener, FocusListener {
MutableCaretEvent(JTextComponent c) {
super(c);
}
final void fire() {
JTextComponent c = (JTextComponent) getSource();
if (c != null) {
Caret caret = c.getCaret();
dot = caret.getDot();
mark = caret.getMark();
c.fireCaretUpdate(this);
}
}
public final String toString() {
return "dot=" + dot + "," + "mark=" + mark;
}
// --- CaretEvent methods -----------------------
public final int getDot() {
return dot;
}
public final int getMark() {
return mark;
}
// --- ChangeListener methods -------------------
public final void stateChanged(ChangeEvent e) {
if (! dragActive) {
fire();
}
}
// --- FocusListener methods --------------------------------
/**
* Stashes the current focused JTextComponent reference
* for JTextAction instances to use if the ActionEvent
* doesn't contain the target text component.
*
* @param e the focus event
* @see JTextAction
* @see FocusListener#focusGained
*/
public void focusGained(FocusEvent e) {
focusedComponent = (JTextComponent) getSource();
}
/**
* Removes reference to focused text component that
* instances of JTextAction use.
*
* @param e the focus event
* @see JTextAction
* @see FocusListener#focusLost
*/
public void focusLost(FocusEvent e) {
// temp focus loss from menus causes problems
//focusedComponent = null;
}
// --- MouseListener methods -----------------------------------
/**
* Requests focus on the associated
* text component, and try to set the cursor position.
*
* @param e the mouse event
* @see MouseListener#mousePressed
*/
public final void mousePressed(MouseEvent e) {
dragActive = true;
}
/**
* Called when the mouse is released.
*
* @param e the mouse event
* @see MouseListener#mouseReleased
*/
public final void mouseReleased(MouseEvent e) {
dragActive = false;
fire();
}
public final void mouseClicked(MouseEvent e) {
}
public final void mouseEntered(MouseEvent e) {
}
public final void mouseExited(MouseEvent e) {
}
private boolean dragActive;
private int dot;
private int mark;
}
class FocusAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
requestFocus();
}
public boolean isEnabled() {
if(isEditable())
return true;
else
return false;
}
}
}
true
, this component is
* enabled; otherwise this component is disabled.
* @see #isEnabled
* @since JDK1.1
*/
public void setEnabled(boolean b) {
super.setEnabled(b);
repaint();
}
/**
* Returns true if the focus can be traversed. This would be false
* for components like a disabled button.
*
* @return true if the focus is traversable
*/
public boolean isFocusTraversable() {
return isEnabled();
}
/**
* Processes any key events that the component itself
* recognizes. This will be called after the focus
* manager and any interested listeners have been
* given a chance to steal away the event. This
* method will only be called is the event has not
* yet been consumed. This method is called prior
* to the keyboard UI logic.
*